#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include "linux_socket.h"

#ifdef _DEBUG

#define ASSERT(flag) \
	if (!flag){ \
		fprintf(stderr, "assert on line %d\n", __LINE__); \
		exit(-1); \
	}
#define VERIFY(flag) \
	if (!flag) { fprintf(stderr, "verify on line %d\n", __LINE__); }
	
#else

#define ASSERT(flag) if (!flag) { return false; }
#define VERIFY(flag) if (!flag)

#endif

int FPV_iLastError;

ftp_scoket FPF_SockCreate(int nSocketPort, int nSocketType)
{
	ftp_scoket s = INVALID_SOCKET;
	s = FPF_Socket(nSocketType);
	if (s!=INVALID_SOCKET)
	{
		int nResult;
		if (FPF_SockBind(s, nSocketPort, NULL))
			return s;
		nResult = FPV_iLastError;
		FPF_SockClose(s);
		s = INVALID_SOCKET;
		FPV_iLastError = nResult;
	}
	FPV_iLastError = errno;
	
	return s;
}

ftp_scoket FPF_Socket(int nSocketType)
{
	ftp_scoket s = socket(PF_INET, nSocketType, 0);
	FPV_iLastError = errno;
	
	return s;
}

bool FPF_SockBind(ftp_scoket s, int nSocketPort, const char* lpszSocketAddress)
{
	int retval;
	struct sockaddr_in sockAddr;
	memset(&sockAddr, 0, sizeof(sockAddr));

	sockAddr.sin_family = AF_INET;
	if (lpszSocketAddress==NULL)
		sockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
	else
	{
		u_int lResult = inet_addr(lpszSocketAddress);
		if (lResult==INADDR_NONE)
		{
			FPV_iLastError = EINVAL;
			return false;
		}
		sockAddr.sin_addr.s_addr = lResult;
	}
	sockAddr.sin_port = htons((u_short)nSocketPort);
	retval = bind(s, (struct sockaddr *)&sockAddr, sizeof(sockAddr));
	FPV_iLastError = errno;
	
	return (SOCKET_ERROR!=retval);
}

bool FPF_SockConnect(ftp_scoket s, const char* lpszHostAddress, int nHostPort)
{
	int retval;
	struct sockaddr_in sockAddr;
	memset(&sockAddr, 0, sizeof(sockAddr));
	sockAddr.sin_family = AF_INET;
	sockAddr.sin_addr.s_addr = inet_addr(lpszHostAddress);
	if (sockAddr.sin_addr.s_addr==INADDR_NONE)
	{
		struct hostent * lphost;
		lphost = gethostbyname(lpszHostAddress);
		if (lphost != NULL)
			sockAddr.sin_addr.s_addr = ((struct in_addr *)lphost->h_addr)->s_addr;
		else
		{
			FPV_iLastError = EINVAL;
			return false;
		}
	}
	sockAddr.sin_port = htons((u_short)nHostPort);
	retval = connect(s, (struct sockaddr *)&sockAddr, sizeof(sockAddr));
	FPV_iLastError = errno;
	
	return (SOCKET_ERROR!=retval);
}

bool FPF_SockListen(ftp_scoket s, int backlog)
{
	int retval = listen(s, backlog);
	FPV_iLastError = errno;
	
	return (SOCKET_ERROR!=retval);
}

ftp_scoket FPF_SockAccept(ftp_scoket s)
{
	ftp_scoket child = accept(s, NULL, NULL);
	FPV_iLastError = errno;
	
	return child;
}

int FPF_SockReceive(ftp_scoket s, void* lpBuf, int nBufLen)
{
	int retval = recv(s, (char*)lpBuf, nBufLen, 0);
	FPV_iLastError = errno;
	
	return retval;
}

int FPF_SockReceiveFrom(ftp_scoket s, void* lpBuf, int nBufLen, char* rSocketAddress, int* rSocketPort)
{
	struct sockaddr_in sockAddr;
	int nSockAddrLen;
	int nResult;

	memset(&sockAddr, 0, sizeof(sockAddr));

	nSockAddrLen = sizeof(sockAddr);
	nResult = recvfrom(s, (char *)lpBuf, nBufLen, 0, (struct sockaddr *)&sockAddr, (size_t*)&nSockAddrLen);
	if(nResult!=SOCKET_ERROR)
	{
		*rSocketPort = ntohs(sockAddr.sin_port);
		strcpy(rSocketAddress, (char *)inet_ntoa(sockAddr.sin_addr));
	}
	FPV_iLastError = errno;
	
	return nResult;
}

int FPF_SockSend(ftp_scoket s, const void* lpBuf, int nBufLen)
{
	int retval = send(s, (char*)lpBuf, nBufLen, 0);
	FPV_iLastError = errno;
	
	return retval;
}

int FPF_SockSendTo(ftp_scoket s, const void* lpBuf, int nBufLen, const char* lpszHostAddress, int nHostPort)
{
	int retval;
	struct sockaddr_in sockAddr;

	memset(&sockAddr,0,sizeof(sockAddr));

	sockAddr.sin_family = AF_INET;

	if (lpszHostAddress==NULL)
		sockAddr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
	else
	{
		sockAddr.sin_addr.s_addr = inet_addr(lpszHostAddress);
		if (sockAddr.sin_addr.s_addr == INADDR_NONE)
		{
			struct hostent * lphost;
			lphost = gethostbyname(lpszHostAddress);
			if (lphost != NULL)
				sockAddr.sin_addr.s_addr = ((struct in_addr *)lphost->h_addr)->s_addr;
			else
			{
				FPV_iLastError = EINVAL;
				return SOCKET_ERROR;
			}
		}
	}

	sockAddr.sin_port = htons((u_short)nHostPort);
	retval = sendto(s, lpBuf, nBufLen, 0, (struct sockaddr *)&sockAddr, sizeof(sockAddr));
	FPV_iLastError = errno;

	return retval;
}

bool FPF_SockShutDown(ftp_scoket s, int nHow)
{
	int retval = shutdown(s, nHow);
	FPV_iLastError = errno;
	
	return (SOCKET_ERROR!=retval);
}

bool FPF_SockSelect(ftp_scoket s, int lEvent, int nMsTimeout)
{
	fd_set readfds, writefds;
	struct timeval tv;
	int iResult = 0;

	FD_ZERO(&readfds);
	if ((lEvent&FD_ACCEPT) || (lEvent&FD_READ) || (lEvent&FD_CLOSE))
		FD_SET(s, &readfds);
	FD_ZERO(&writefds);
	if ((lEvent&FD_CONNECT) || (lEvent&FD_WRITE))
		FD_SET(s, &writefds);
	tv.tv_sec = nMsTimeout/1000;
	tv.tv_usec = (nMsTimeout%1000)*1000;
	iResult = select(s+1, &readfds, &writefds, NULL, &tv);
	FPV_iLastError = errno;
	
	return (iResult>0);
}

bool FPF_SockGetPeerName(ftp_scoket s, char* rPeerAddress, int* rPeerPort)
{
	struct sockaddr_in sockAddr;
	int  nSockAddrLen;
	bool bResult = false;

	memset(&sockAddr, 0, sizeof(sockAddr));
	nSockAddrLen = sizeof(sockAddr);
	bResult = 
		(SOCKET_ERROR!=getpeername(s, (struct sockaddr *)&sockAddr, (size_t*)&nSockAddrLen));
	FPV_iLastError = errno;
	if (bResult)
	{
		*rPeerPort = ntohs(sockAddr.sin_port);
		FPV_iLastError = errno;
		strcpy(rPeerAddress, (char *)inet_ntoa(sockAddr.sin_addr));
	}
	return bResult;
}

bool FPF_SockGetSockName(ftp_scoket s, char* rSocketAddress, int* rSocketPort)
{
	struct sockaddr_in sockAddr;
	int  nSockAddrLen;
	bool bResult = false;

	memset(&sockAddr, 0, sizeof(sockAddr));
	nSockAddrLen = sizeof(sockAddr);
	bResult = 
		(SOCKET_ERROR!=getsockname(s, (struct sockaddr *)&sockAddr, (size_t*)&nSockAddrLen));
	FPV_iLastError = errno;
	if (bResult)
	{
		*rSocketPort = ntohs(sockAddr.sin_port);
		FPV_iLastError = errno;
		strcpy(rSocketAddress, (char *)inet_ntoa(sockAddr.sin_addr));
	}
	return bResult;
}

bool FPF_SockGetSockOpt(ftp_scoket s, int nLevel, int nOptionName, void* lpOptionValue, int* lpOptionLen)
{
	int retval = getsockopt(s, nLevel, nOptionName, (char*)lpOptionValue, (size_t*)lpOptionLen);
	FPV_iLastError = errno;
	
	return (SOCKET_ERROR!=retval);
}

bool FPF_SockSetSockOpt(ftp_scoket s, int nLevel, int nOptionName, const void* lpOptionValue, int nOptionLen)
{
	int retval = setsockopt(s, nLevel, nOptionName, (char*)lpOptionValue, nOptionLen);
	FPV_iLastError = errno;
	
	return (SOCKET_ERROR!=retval);
}

bool FPF_SockIOCtl(ftp_scoket s, int iCommand, int* lpArgument)
{
	int retval = ioctl(s, iCommand, lpArgument);
	FPV_iLastError = errno;
	
	return (SOCKET_ERROR!=retval);
}

int  FPF_SockGetLastError()
{
	return FPV_iLastError;
}
